﻿package  {
	import flash.events.*;
	import flash.display.Sprite;
	import flash.utils.getTimer;
	import flash.net.URLRequest;
	import flash.net.FileFilter;
	import flash.net.FileReference;
	import flashx.textLayout.events.SelectionEvent;
	import flashx.textLayout.events.UpdateCompleteEvent;
	import flash.net.drm.VoucherAccessInfo;
	
	public class UploadFile extends Sprite {
		
		private var active: Active;
		private var startTime: Number;
		private var uploadURL: URLRequest;
		private var uploading: Boolean;
		private var fileReference: FileReference;
		
		public function UploadFile() {
			initInterfaces();
			initFileReference();
		}
		
		// 格式化時間
		private function formatTime(date: Date, type: String = 'yyyy-mm-dd hh:ii:ss'): String {
			type = type.toLowerCase();
			if (/[^ymdhis]+/.test(type)) {
				return '';
			}
			
			var year: String   = date.getFullYear().toString();
			var month: String  = (date.getMonth() + 1).toString();
			var day: String    = date.getDate().toString();
			var hour: String   = date.getHours().toString();
			var minute: String = date.getMinutes().toString();
			var second: String = date.getSeconds().toString();
			
			var replace: Function = function(source: String, str: String, time: String) {
				var regExp: RegExp = new RegExp(str + '+');
				return source.replace(regExp, function(a) { return ('0000' + time).slice(a.indexOf('y') > -1 ? -4 : -2); });
			}
			
			type = replace(type, 'y', year);
			type = replace(type, 'm', month);
			type = replace(type, 'd', day);
			type = replace(type, 'h', hour);
			type = replace(type, 'i', minute);
			type = replace(type, 's', second);
			
			return type;
		}
		
		// 初始化AS與JS之間的接口
		private function initInterfaces(): void {
			active = new Active();
			active.loadJSAPI({
				callBack  : 'UploadFile.config.progress',
				setLoaded : 'UploadFile.setLoadStatus',
				showMsg   : 'UPloadFile.showMsg',
				url       : 'UploadFile.getURL'
			});
			active.publishASAPI({ cancel : cancel });
			active.jsMethod.setLoaded();
			
			stage.addEventListener(MouseEvent.CLICK, browserHandler);
		}
		
		// 初始化FileReference對象
		private function initFileReference(): void {
			fileReference = new FileReference();
			
			fileReference.addEventListener(Event.CANCEL, cancelHandler);
			fileReference.addEventListener(Event.COMPLETE, completeHandler);
			fileReference.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
			fileReference.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			fileReference.addEventListener(Event.OPEN, openHandler);
			fileReference.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			fileReference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			fileReference.addEventListener(Event.SELECT, selectHandler);
			fileReference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadCompleteDataHandler);
			uploading = false;
		}
		
		// 取消文件上傳所觸發的事件，此事件只在解雇關閉選擇文件聖誕框時觸發，調用cancel方法不會觸發
		private function cancelHandler(event: Event): void {
			trace('取消了上傳文件的選擇。。。');
			active.jsMethod.callBack({ finish : true, event : 'CANCEL' });
			uploading = false;
		}
		
		// 上傳完成，伺服器端返回200狀態時觸發此事件
		private function completeHandler(event: Event): void {
			trace('文件上傳成功。。。');
			active.jsMethod.callBack({ finish : true, event : 'COMPLETE' });
			uploading = false;
		}
		
		// 上傳失敗時觸發此事件
		private function httpStatusHandler(event: HTTPStatusEvent): void {
			trace('上傳文件失敗，狀態碼：' + event.status);
			active.jsMethod.callBack({ finish : true, event : 'HTTPSTATUS', status : event.status });
			uploading = false;
		}
		
		// 上傳失敗時且有可用來描述失敗的HTTP狀態碼時觸發此事件，如需要身份驗證時
		private function ioErrorHandler(event: IOErrorEvent): void {
			trace('上傳文件失敗。。。');
			active.jsMethod.callBack({ finish : true, event : 'IOERROR', msg : event.text });
			uploading = false;
		}
		
		// 開始上傳文件時觸發
		private function openHandler(event: Event): void {
			if (uploading) {
				active.jsMethod.showMsg('uploading');
				return void;
			}
			trace('開始上傳。。。');
			active.jsMethod.callBack({ finish : false, event : 'OPEN' });
			uploading = true;
		}
		
		// 上傳操作期間定時調度此事件
		private function progressHandler(event: ProgressEvent): void {
			var total: Number     = event.bytesTotal;
			var loaded: Number    = event.bytesLoaded;
			var time: Number      = getTimer() - startTime;
			var speed: Number     = Math.ceil(loaded / time);                    // KB/s,因為這里的時間是毫秒，因為直接按K來計算
			var remaining: Number = Math.floor((total - loaded) / speed / 1000);  // 剩餘的秒數
			
			trace('上傳中。已上傳：' + event.bytesLoaded);
			active.jsMethod.callBack({
				finish      : false,
				event       : 'PROGRESS',
				bytesTotal  : total,              // 總文件大小
				bytesLoaded : loaded,             // 已經上傳的大小
				speed       : speed + 'KB/s',     // 上傳速度
				remaining   : remaining           // 剩餘的時間，單位毫秒
			});
			uploading = true;
		}
		
		// 提交到外部伺服器時，安全沙箱起作用時觸發。（大體可以這麼理解）
		private function securityErrorHandler(event: SecurityErrorEvent): void {
			trace('跨域操作被拒絕。。。');
			active.jsMethod.callBack({ finish : true, event : 'SECURITYERROR', msg : event.text });
			uploading = false;
		}
		
		// 選擇了要上傳的文件時觸發
		private function selectHandler(event: Event): void {
			var fileName: String         = fileReference.name;
			var fileSize: uint           = fileReference.size;
			var fileType: String         = fileReference.type;
			var creationDate: String     = formatTime(fileReference.creationDate);
			var modificationDate: String = formatTime(fileReference.modificationDate);
			
			trace('選擇了要上傳的文件，文件信息：');
			trace('文件名：' + fileName);
			trace('文件大小（字節）：' + fileSize);
			trace('文件類型：' + fileType);
			trace('創建時間：' + creationDate);
			trace('最後修改時間：' + modificationDate);
			
			active.jsMethod.callBack({
				finish           : false,
				event            : 'SELECT',
				fileName         : fileName,
				fileSize         : fileSize,
				fileType         : fileType,
				creationDate     : creationDate,
				modificationDate : modificationDate
			});
			startTime = getTimer();
			fileReference.upload(uploadURL);
		}
		
		// 成功上傳並且從伺服器端接收數據后觸發。
		private function uploadCompleteDataHandler(event: DataEvent): void {
			trace('上傳成功。伺服器端返回的數據：' + event.data);
			
			active.jsMethod.callBack({ finish : true, event : 'UPLOADCOMPLETEDATA', data : event.data });
			uploading = false;
		}
		
		// 單擊舞臺則選擇文件，先從JS中獲取到遠程的URL
		private function browserHandler(event: MouseEvent): void {
			uploadURL = new URLRequest(active.jsMethod.url());
			fileReference.browse();
		}
		
		// 取消上傳
		public function cancel(): void {
			fileReference.cancel();
		}
	}
}
